package online.chazi.netkit;

import io.netty.channel.Channel;
import io.netty.channel.ChannelId;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.timeout.IdleState;
import lombok.Data;
import online.chazi.netkit.action.Action;
import online.chazi.netkit.action.ActionFilter;
import online.chazi.netkit.annotation.DefaultInjectProxy;
import online.chazi.netkit.annotation.InjectProxy;
import online.chazi.netkit.listener.DefaultChanelListener;
import online.chazi.netkit.listener.DefaultSessionListener;
import online.chazi.netkit.listener.NetkitChanelListener;
import online.chazi.netkit.listener.NetkitSessionListener;
import online.chazi.netkit.messaging.DefaultMessageEncrypt;
import online.chazi.netkit.messaging.MessageEncrypt;

import java.time.Duration;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 说明：作者很懒，什么都没留下
 * Created by 叉子同学 on 2020-04-23 19:42
 */
@Data
public class NetkitContext {

    private final Map<Integer, Class<? extends Action>> registeredActionsClass = new ConcurrentHashMap<>();
    private final List<ActionFilter> actionFilters = new LinkedList<>();
    private final Map<String, Object> attributes = new ConcurrentHashMap<>();
    private final SessionGroup sessionGroup = new SessionGroup();

    private NetkitChanelListener chanelListener = new DefaultChanelListener();
    private NetkitSessionListener sessionListener = new DefaultSessionListener();

    private Duration sessionTimeout = Duration.ofSeconds(12);
    private IdleState sessionIdleState = IdleState.READER_IDLE;

    private int maxConnections = 30000;

    //加密消息
    private MessageEncrypt messageEncrypt = new DefaultMessageEncrypt();

    //找不到action时默认使用的, 404
    private Action notFoundAction;

    //自动注入代理
    private InjectProxy injectProxy = new DefaultInjectProxy();

    //是否使用cached
    private boolean usingActionCached;

    private NioEventLoopGroup bossGroup;
    private NioEventLoopGroup workGroup;
    private ExecutorService workerExecutor = Executors.newCachedThreadPool();

    public NetkitContext(int cpuCoreCount) {
        if (cpuCoreCount <= 0) cpuCoreCount = 2;
        this.bossGroup = new NioEventLoopGroup(cpuCoreCount);
        this.workGroup = new NioEventLoopGroup(cpuCoreCount * 8);
    }

    public void setSessionIdleTimeout(IdleState idleState, Duration sessionTimeout) {
        this.sessionIdleState = idleState;
        this.sessionTimeout = sessionTimeout;
    }

    public int getSessionSize() {
        return this.sessionGroup.size();
    }

    public Session getSession(Channel channel) {
        return this.getSession(channel.id());
    }

    public Session getSession(ChannelId id) {
        return this.sessionGroup.get(id);
    }

    /**
     * 这个方法是提供给客户端建立session用的。
     *
     * @param channel 与服务器端建立的通道
     * @return 会话
     */
    protected Session createSession(Channel channel) {
        if (channel == null) {
            return null;
        } else {
            Session session = this.getSession(channel);
            if (session != null) {
                session.setSessionListener(sessionListener);
                return session;
            } else {
                Session s = new Session(this, channel);
                s.setSessionListener(sessionListener);
                this.sessionGroup.add(s.getSessionId(), s);
                s.onCreated();
                return s;
            }
        }
    }

    public Collection<Session> getAllSessions() {
        return this.sessionGroup.sessions();
    }

    public void release() {
        this.sessionGroup.close();
        this.bossGroup.shutdownGracefully();
        this.workGroup.shutdownGracefully();
        this.workerExecutor.shutdownNow();
    }

    public void registerAction(int messageId, Class<? extends Action> action) {
        synchronized (this.registeredActionsClass) {
            Class<? extends Action> a = this.registeredActionsClass.get(messageId);
            if (a != null) {
                this.registeredActionsClass.remove(messageId);
            }
            this.registeredActionsClass.put(messageId, action);
        }
    }

    public Class<? extends Action> lookupRegisteredActionClass(int messageId) {
        return this.registeredActionsClass.get(messageId);
    }

    public List<ActionFilter> getActionFilterList() {
        return Collections.unmodifiableList(this.actionFilters);
    }

    public void addActionFilter(ActionFilter filter) {
        if (filter == null) {
            throw new NullPointerException("action filter object was null.");
        } else {
            this.actionFilters.add(filter);
        }
    }

}
